/*
 * @Author: 晟松
 * @Date: 2021-12-10 22:31:02
 * @LastEditors: 晟松
 * @LastEditTime: 2022-03-09 16:46:20
 * @FilePath: \mini-vue3d:\Code\node\koa2\volunteer-service\src\main\service\VolService.js
 * @Description: 志愿服务的一些操作
 */

import sequelize from "@main/db";
import { Op } from "sequelize";
import { isNull, isBlank } from "@main/util/ValidateUtil";
import { ParamError, LoginError, NotFoundError } from "@main/common/CommonError";
import { encryptPassword, checkPassword } from "@main/util/PasswordUtil";
import config from "@main/config";
import blacklist from "blacklist";
import whiteListed from "whitelisted";
import jwt from "jsonwebtoken";
import enums from "@main/config/enums";
import { toEnumNum, toEnumStr } from "@main/util/EnumsUtil";
import { type } from "koa/lib/response";
import dayjs from "dayjs";
import SchoolService from "./SchoolService";
import { nowTime } from "@main/util/TimeUtils";
import UserService from "./UserService";
// 使用区块链进行存储更新
import remix from "@main/web3js/web3";
import Web3Service from "./Web3Service";

// 获取所有定义的model
const models = sequelize.models;

class VolService {
    /**
     * @description: 居民添加志愿服务
     * @param {*} params
     * @return {*}
     */
    static async add(params) {
        // 志愿服务所需的字段，id，identity为发送者的id和身份
        let { id: initiatorId, identity: initiatorIdentity, timeIntegral, duration } = params;

        let volInfo = blacklist(params, "id", "identity");
        // volInfo.state = toEnumNum(volInfo.state, "state");
        // 添加志愿服务时的状态直接指定为等待审核
        volInfo.state = enums["state"].pendingReview;
        initiatorIdentity = toEnumNum(initiatorIdentity, "identity");

        console.log("volInfo: ", volInfo);
        // 将一些别的字段混合进volinfo中
        Object.assign(volInfo, {
            initiatorIdentity,
            initiatorId,
        });
        // 是否多加了自己的时间积分
        let addTimeIntegral = duration * 10 - timeIntegral;
        if (addTimeIntegral > 0) {
            let userInfo = await models[initiatorIdentity].findOne({
                where: {
                    id: initiatorId,
                },
            });
            // 获得剩下的积分数
            let differTimeIntergral = userInfo.timeIntegral - addTimeIntegral;
            if (differTimeIntergral < 0) {
                throw new ParamError("时间积分不足");
            }

            await models[initiatorIdentity].update(
                {
                    timeIntegral: differTimeIntergral,
                },
                {
                    where: {
                        id: userInfo.id,
                    },
                }
            );
        }
        let res = await models["vol"].create(volInfo);
        
        // 存储一下到区块链中
        await Web3Service.updateOne("vol", res.id);

        return res;
    }
    /**
     * @description: 删除自己的一个志愿服务。
     * @param {*} params
     * @return {*}
     */
    static async delete(params, user) {
        // 发起者的id,志愿服务的id
        let { volId, id: userId, identity: initiatorIdentity } = params;
        // initiatorIdentity传来时应该是个 字符串如：'student'
        initiatorIdentity = toEnumNum(initiatorIdentity, "identity");
        let updateState = {
            state: enums.state.cancelled,
        };
        // 将状态变为已取消进行一个类似删除的操作
        return await models.vol.update(updateState, {
            where: {
                id: volId,
                initiatorId: userId,
                initiatorIdentity,
            },
        });
        // return await models["vol"].destroy({
        //     where: {
        //         id: volId,
        //         initiatorId: userId,
        //         initiatorIdentity,
        //     },
        // });
    }
    /**
     * @description: 得到用户所有的志愿服务的信息，可分页
     * @param {*} params
     * @return {*}
     */
    static async getUserAllVol(params) {
        // id为发起者id
        let { id, identity, limit, offset } = params;
        if (!isNull(limit)) limit -= 0;
        if (!isNull(offset)) offset -= 0;
        return await models["vol"].findAll({
            where: {
                [Op.or]: [
                    {
                        initiatorId: id,
                        initiatorIdentity: toEnumNum(identity, "identity"),
                    },
                    {
                        recipientId: id,
                        recipientIdentity: toEnumNum(identity, "identity"),
                    },
                ],
            },
            limit,
            offset,
            order: [["updatedAt", "DESC"]], //根据更新时间进行降序排序
        });
    }
    static async getVolInfo(params) {
        // id为志愿服务
        let { volId: id } = params;
        return await models["vol"].findOne({
            where: {
                id,
            },
        });
    }

    static async getVolList(params) {
        // id为志愿服务
        let { limit, offset } = params;
        if (!isNull(limit)) limit -= 0;
        if (!isNull(offset)) offset -= 0;
        let volList = await models["vol"].findAll({
            limit,
            offset,
            where: {
                state: enums["state"]["approved"],
                time: {
                    [Op.gt]: nowTime(), // 要志愿服务的时间也要大于当前时间
                },
            },
        });
        // 返回志愿服务的状态为approved即审核通过这一个状态的志愿服务到首页里面去
        // return volList.filter((vol) => {
        //     if (toEnumStr(vol.state, "state") == "approved") {
        //         return true;
        //     }
        // });
        return volList;
    }

    static async update(params) {
        // id为志愿服务的id
        let { volId: id } = params;
        let updateInfo = blacklist(params, "volId");
        let res =  await models["vol"].update(updateInfo, {
            where: {
                id,
            },
        });

         // 存储一下到区块链中
         await Web3Service.updateOne("vol", id);

         return res;
    }
    /**
     * @description: 居委会审核志愿服务
     * @param {*} params
     * @return {*}
     */
    static async reviewVol(params) {
        // isReturned表示是否打回，为false就是审核通过
        let { volId: id, isReturned, reviewOpinion } = params;
        // let updateInfo = blacklist(params, "volId", "isReturned");
        // 默认是通过的，且只能修改state这一个信息
        let updateInfo = { state: enums.state["approved"] };

        // 最核心的是这个志愿服务的状态问题，是被退回，还是被审核通过
        // updateInfo.state = toEnumNum(updateInfo.state, "state");
        if (isReturned) {
            updateInfo.state = enums.state["returned"];
        }
        if (!isBlank(reviewOpinion)) {
            updateInfo.reviewOpinion = reviewOpinion;
        }

        let res =  await models["vol"].update(updateInfo, {
            where: {
                id,
            },
        });

         // 存储一下到区块链中
         await Web3Service.updateOne("vol", id);

         return res;
    }
    /**
     * @description: 学生或居民领取志愿服务
     * @param {*}
     * @return {*}
     */
    static async receiveVol(params) {
        // id为领取者的id
        let { id, identity, volId } = params;
        let recipient = await models[identity].findOne({
            where: {
                id,
            },
        });
        if (!recipient.available) {
            return {
                data: "请确保管理员审核通过",
                error: 403,
            };
        }
        // 注意要改变志愿服务的状态
        let updateInfo = {
            recipientIdentity: toEnumNum(identity, "identity"),
            recipientId: id,
            state: enums.state["alreadyClaimed"],
            recipientTime: nowTime(),
        };
        let res = await models["vol"].update(updateInfo, {
            where: {
                id: volId,
            },
        });
        // 存储一下到区块链中
        await Web3Service.updateOne("vol", volId);

        // return res;
        return {
            data: res,
            success: 200,
        };
    }

    /**
     * @description: 院级管理员获得自己所有学生的志愿服务记录
     * @param {*} params
     * @return {*}
     */
    static async getCollegeAllStudentVol(params) {
        // id为院级管理员id
        let { id, limit, offset } = params;
        if (!isNull(limit)) limit -= 0;
        if (!isNull(offset)) offset -= 0;

        // 先获取所有自己的学生，通过审核的
        let allCollegeStudent = await models["student"].findAll({
            where: {
                schoolId: id,
                available: true,
            },
            // attributes: ["id"], // 限制值返回stu的id号
        });

        let allStudentVolInfo = [];
        // 总志愿服务的数量
        let allLen = {
            belongUser: allCollegeStudent.length,
            vol: 0,
        };
        // vol的一些信息，同时携带其发起者和领取者的信息，如果有的话
        let pureAllVol = [];
        for (let i = 0; i < allCollegeStudent.length; i++) {
            let stu = allCollegeStudent[i];
            let { id, identity } = stu;
            let studentVolInfo = await models["vol"].findAll({
                where: {
                    // 发起和领取都可以
                    [Op.or]: [
                        {
                            recipientId: stu.id,
                            recipientIdentity: enums.identity["student"],
                        },
                        {
                            initiatorId: stu.id,
                            initiatorIdentity: enums.identity["student"],
                        },
                    ],
                },
            });
            // 将用户信息也一同加到志愿服务的信息里面去
            for (let j = 0; j < studentVolInfo.length; j++) {
                let vol = studentVolInfo[j];
                let { recipientId, recipientIdentity, initiatorId, initiatorIdentity } = vol;
                // 领取者信息
                if (!isBlank(recipientId)) {
                    // 注意需要存在dataValues属性里面进行返回
                    vol.dataValues.recipientUser = await UserService.findUserInfo({
                        id: recipientId,
                        identity: toEnumStr(recipientIdentity, "identity"),
                    });
                }
                // 发起者信息
                if (!isBlank(initiatorId)) {
                    vol.dataValues.initiatorUser = await UserService.findUserInfo({
                        id: initiatorId,
                        identity: toEnumStr(initiatorIdentity, "identity"),
                    });
                }
            }
            pureAllVol.push(...studentVolInfo);
            console.log("pureAllVol: ", pureAllVol);
            allLen.vol += studentVolInfo.length;
            // allStudentVolInfo就是学生信息里面带着自己的志愿服务信息
            allStudentVolInfo.push(Object.assign(stu.dataValues, { studentVolInfo }));
        }

        // 下面这种方法不行，似乎不能使用map或foreach等方法，这种的话，无法await拦截到这个返回值，会在return后，才得到数据库返回值
        /* allCollegeStudent.forEach(async (student) => {
            // 获得原始数据
            let stu = JSON.parse(JSON.stringify(student.dataValues));
            let studentVolInfo = await models["vol"].findAll({
                where: {
                    recipientId: stu.id,
                    recipientIdentity: enums.identity["student"],
                },
            });
            console.log("studentVolInfo: ", studentVolInfo.length);
            // let res = Object.assign(stu, studentVolInfo);
            stu.volInfo = studentVolInfo;
            // console.log("stu: ", stu);
            allVolInfo.push(stu);
            // console.log("allVolInfo---------: ", allVolInfo);
        }); */
        return { allStudentVolInfo, allLen, pureAllVol };
    }

    /**
     * @description: 查询满足条件的自己院里面的学生的所有志愿服务
     * @param {*} params
     * @return {*}
     */
    static async searchCollegeAllStudentVol(params) {
        // id为院级管理员id
        let { id, limit, offset, stuName, stuPhone, volSatate } = params;
        if (!isNull(limit)) limit -= 0;
        if (!isNull(offset)) offset -= 0;

        // 先获取所有自己的学生，通过审核的
        let allCollegeStudent = await models["student"].findAll({
            where: {
                schoolId: id,
                available: true,
            },
            include: models["login"],
            // attributes: ["id"], // 限制值返回stu的id号
        });

        let allVolInfo = [];
        // 总志愿服务的数量
        let allLen = {
            belongUser: allCollegeStudent.length,
            vol: 0,
        };
        // 只有vol的一些信息
        let pureAllVol = [];
        for (let i = 0; i < allCollegeStudent.length; i++) {
            let stu = allCollegeStudent[i];
            let { id, identity, userName } = stu;
            // 如果有学生名字的限制，name就只查这一个学生的志愿服务信息
            if (!isBlank(stuName)) {
                if (!userName.includes(stuName)) continue;
            }
            // 同理，手机号也是如此
            if (!isBlank(stuPhone)) {
                if (!stu.login.phone.includes(stuphone)) continue;
            }
            let studentVolInfo = [];
            // state这里特殊处理一下，因为在where条件里面，只要有，不论是null还是undefined都会进行匹配
            if (!isBlank(volSatate)) {
                // 判断一下是 '1'这种还是 'pendingReview'这种.注意一下 isNaN和 Number.isNaN()的差别
                if (Number.isNaN(Number(volSatate))) {
                    // 是 'pendingReview'这种
                    volSatate = toEnumNum(volSatate, "state");
                }
                studentVolInfo = await models["vol"].findAll({
                    where: {
                        // 发起和领取都可以
                        [Op.or]: [
                            {
                                recipientId: stu.id,
                                recipientIdentity: enums.identity["student"],
                            },
                            {
                                initiatorId: stu.id,
                                initiatorIdentity: enums.identity["student"],
                            },
                        ],
                        state: volSatate,
                    },
                });
            } else {
                studentVolInfo = await models["vol"].findAll({
                    where: {
                        // 发起和领取都可以
                        [Op.or]: [
                            {
                                recipientId: stu.id,
                                recipientIdentity: enums.identity["student"],
                            },
                            {
                                initiatorId: stu.id,
                                initiatorIdentity: enums.identity["student"],
                            },
                        ],
                    },
                });
            }
            // 将用户信息也一同加到志愿服务的信息里面去
            for (let j = 0; j < studentVolInfo.length; j++) {
                let vol = studentVolInfo[j];
                let { recipientId, recipientIdentity, initiatorId, initiatorIdentity } = vol;
                // 领取者信息
                if (!isBlank(recipientId)) {
                    // 注意需要存在dataValues属性里面进行返回
                    vol.dataValues.recipientUser = await UserService.findUserInfo({
                        id: recipientId,
                        identity: toEnumStr(recipientIdentity, "identity"),
                    });
                }
                // 发起者信息
                if (!isBlank(initiatorId)) {
                    vol.dataValues.initiatorUser = await UserService.findUserInfo({
                        id: initiatorId,
                        identity: toEnumStr(initiatorIdentity, "identity"),
                    });
                }
            }
            pureAllVol.push(...studentVolInfo);
            console.log("pureAllVol: ", pureAllVol);
            allLen.vol += studentVolInfo.length;
            allVolInfo.push(Object.assign(stu.dataValues, { studentVolInfo }));
        }

        return { allVolInfo, allLen, pureAllVol };
    }

    /**
     * @description: 居委会获得所有居民志愿服务
     * @param {*} params
     * @return {*}
     */
    static async getAllResidentVol(params) {
        // id为居委会id
        let { id, limit, offset } = params;
        if (!isNull(limit)) limit -= 0;
        if (!isNull(offset)) offset -= 0;

        // 先获取所有自己的居民
        let allResident = await models["resident"].findAll({
            where: {
                communityId: id,
                available: true,
            },
            // attributes: ["id"], // 限制值返回stu的id号
        });
        let allLen = {
            belongUser: allResident.length,
            vol: 0,
        };
        let allVolInfo = [];
        let pureAllVol = [];
        for (let i = 0; i < allResident.length; i++) {
            let resident = allResident[i];
            let residentVolInfo = await models["vol"].findAll({
                where: {
                    // 可能是发起者，也可能是领取者
                    [Op.or]: [
                        {
                            recipientId: resident.id,
                            recipientIdentity: enums.identity["resident"],
                        },
                        {
                            initiatorId: resident.id,
                            initiatorIdentity: enums.identity["resident"],
                        },
                    ],
                },
            });
            // 将用户信息也一同加到志愿服务的信息里面去
            for (let j = 0; j < residentVolInfo.length; j++) {
                let vol = residentVolInfo[j];
                let { recipientId, recipientIdentity, initiatorId, initiatorIdentity } = vol;

                // 领取者信息
                if (!isBlank(recipientId)) {
                    // 注意需要存在dataValues属性里面进行返回
                    vol.dataValues.recipientUser = await UserService.findUserInfo({
                        id: recipientId,
                        identity: toEnumStr(recipientIdentity, "identity"),
                    });
                }
                // 发起者信息
                if (!isBlank(initiatorId)) {
                    vol.dataValues.initiatorUser = await UserService.findUserInfo({
                        id: initiatorId,
                        identity: toEnumStr(initiatorIdentity, "identity"),
                    });
                }
            }

            pureAllVol.push(...residentVolInfo);
            allLen.vol += residentVolInfo.length;
            allVolInfo.push(Object.assign(resident.dataValues, { residentVolInfo }));
        }
        return { allVolInfo, allLen, pureAllVol };
    }

    /**
     * @description: 居委会查询满足条件的自己里面的居民的所有志愿服务
     * @param {*} params
     * @return {*}
     */
    static async searchAllResidentVol(params) {
        // id为居委会id
        let { id, limit, offset, residentName, residentPhone, volSatate } = params;
        if (!isNull(limit)) limit -= 0;
        if (!isNull(offset)) offset -= 0;

        // 先获取所有自己的学生，通过审核的
        let allResident = await models["resident"].findAll({
            where: {
                communityId: id,
                available: true,
            },
            include: models["login"],
        });

        let allVolInfo = [];
        // 总志愿服务的数量
        let allLen = {
            belongUser: allResident.length,
            vol: 0,
        };
        // 只有vol的一些信息
        let pureAllVol = [];
        for (let i = 0; i < allResident.length; i++) {
            let resident = allResident[i];
            let { id, identity, userName } = resident;
            // 如果有学生名字的限制，name就只查这一个学生的志愿服务信息
            if (!isBlank(residentName)) {
                if (!userName.includes(residentName)) continue;
            }
            // 同理，手机号也是如此
            if (!isBlank(residentPhone)) {
                if (!resident.login.phone.includes(residentPhone)) continue;
            }
            let residentVolInfo = [];
            // state这里特殊处理一下，因为在where条件里面，只要有，不论是null还是undefined都会进行匹配
            if (!isBlank(volSatate)) {
                // 判断一下是 '1'这种还是 'pendingReview'这种.注意一下 isNaN和 Number.isNaN()的差别
                if (Number.isNaN(Number(volSatate))) {
                    // 是 'pendingReview'这种
                    volSatate = toEnumNum(volSatate, "state");
                }
                residentVolInfo = await models["vol"].findAll({
                    where: {
                        // 发起和领取都可以
                        [Op.or]: [
                            {
                                recipientId: resident.id,
                                recipientIdentity: enums.identity["resident"],
                            },
                            {
                                initiatorId: resident.id,
                                initiatorIdentity: enums.identity["resident"],
                            },
                        ],
                        state: volSatate,
                    },
                });
            } else {
                residentVolInfo = await models["vol"].findAll({
                    where: {
                        // 发起和领取都可以
                        [Op.or]: [
                            {
                                recipientId: resident.id,
                                recipientIdentity: enums.identity["resident"],
                            },
                            {
                                initiatorId: resident.id,
                                initiatorIdentity: enums.identity["resident"],
                            },
                        ],
                    },
                });
            }
            // 将用户信息也一同加到志愿服务的信息里面去
            for (let j = 0; j < residentVolInfo.length; j++) {
                let vol = residentVolInfo[j];
                let { recipientId, recipientIdentity, initiatorId, initiatorIdentity } = vol;
                // 领取者信息
                if (!isBlank(recipientId)) {
                    // 注意需要存在dataValues属性里面进行返回
                    vol.dataValues.recipientUser = await UserService.findUserInfo({
                        id: recipientId,
                        identity: toEnumStr(recipientIdentity, "identity"),
                    });
                }
                // 发起者信息
                if (!isBlank(initiatorId)) {
                    vol.dataValues.initiatorUser = await UserService.findUserInfo({
                        id: initiatorId,
                        identity: toEnumStr(initiatorIdentity, "identity"),
                    });
                }
            }
            pureAllVol.push(...residentVolInfo);
            console.log("pureAllVol: ", pureAllVol);
            allLen.vol += residentVolInfo.length;
            allVolInfo.push(Object.assign(resident.dataValues, { residentVolInfo }));
        }

        return { allVolInfo, allLen, pureAllVol };
    }

    /**
     * @description: 志愿服务领取者发起反馈
     * @param {*} params
     * @return {*}
     */
    static async feedbackVol(params) {
        let { volId: id, feedback } = params;
        // 状态变为等待评价
        let updateInfo = {
            feedback,
            state: enums.state["toBeEvaluated"],
        };
        let res =  await models["vol"].update(updateInfo, {
            where: {
                id,
            },
        });

        // 存储一下到区块链中
        await Web3Service.updateOne("vol", id);

        return res;
    }
    /**
     * @description: 发起者评价整个志愿服务，包括分数等
     * @param {*} params
     * @return {*}
     */
    static async evaluateVol(params) {
        let { volId: id, evaluation, evaluationScore } = params;
        let updateInfo = {
            state: enums.state["completed"],
            evaluation,
            evaluationScore,
        };
        await models["vol"].update(updateInfo, {
            where: {
                id,
            },
        });

        // 存储一下到区块链中
        await Web3Service.updateOne("vol", id);


        let volInfo = await models["vol"].findOne({
            where: {
                id,
            },
        });
        let { recipientIdentity, recipientId } = volInfo;
        console.log("volInfo: ", volInfo);
        recipientIdentity = toEnumStr(recipientIdentity, "identity");

        console.log("recipientIdentity: ", recipientIdentity);
        let recipientUser = await models[recipientIdentity].findOne({
            where: {
                id: recipientId,
            },
        });
        // 记得把时间积分发给领取者
        let res =  await models[recipientIdentity].update(
            {
                timeIntegral: recipientUser.timeIntegral + volInfo.timeIntegral,
            },
            {
                where: {
                    id: recipientId,
                },
            }
        );

        // 存储一下到区块链中
        await Web3Service.updateOne(recipientIdentity, recipientId);

        return res;
    }
    /**
     * @description: 模糊查询满足条件且未被领取的志愿服务，主要是通过title，事件，地点，特殊要求这几类进行查询
     * @param {*} params
     * @return {*}
     */
    static async searchVol(params) {
        let { search } = params;
        let allVol = await models["vol"].findAll({
            where: {
                state: enums.state["approved"],
                time: {
                    [Op.gt]: nowTime(), // 要志愿服务的时间也要大于当前时间
                },
            },
        });
        let res = allVol.filter((vol) => {
            let dataValues = vol.dataValues;
            let { title, event, demand, place } = dataValues;
            // console.log('title, event, demand, place: ', title, event, demand, place);
            if (title.includes(search) || event.includes(search) || (demand && demand.includes(search)) || place.includes(search)) {
                return true;
            }
        });
        return res;
    }
}

export default VolService;
